/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.flexengine.view;

import com.flexengine.config.FlexDisplayMode;
import com.flexengine.engine.FlexEngine;
import com.flexengine.logger.Logger;
import java.awt.AWTException;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.awt.event.WindowListener;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;

/**
 * @author Luann R. Athayde
 * @version 1.0
 * @since 1.0
 */
public class GameWindow {

    // Globals...
    // -- Default size...
    public static final int DEFAULT_WIDTH  = 1280;
    public static final int DEFAULT_HEIGHT = 720;
    // -- Display mode...
    public static final int DM_PERFORMANCE = 0x0A;
    public static final int DM_BALANCED    = 0x0B;
    public static final int DM_QUALITY     = 0x0C;
    // -- Window attributs...
    protected int                  width;
    protected int                  height;
    protected int                  bitDepth;
    protected int                  refreshRate;
    protected DisplayMode[]        displayModes;
    // -- Environment attributs...
    protected GraphicsEnvironment  gEnv;
    protected GraphicsDevice       gDev;
    private JFrame                 window;
    private boolean                fullScreen;
    // -- Especifields att...
    private boolean                optimized;

    public GameWindow() {
        this.width = DEFAULT_WIDTH;
        this.height = DEFAULT_HEIGHT;
        this.bitDepth = DisplayMode.BIT_DEPTH_MULTI;
        this.refreshRate = DisplayMode.REFRESH_RATE_UNKNOWN;
        //----------------------------------------------------------------------
        this.window = new JFrame();
        this.window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        this.window.setLayout(null);
        this.window.setBackground(Color.black);
        this.window.getContentPane().setBackground(Color.black);
        this.window.setResizable(false);
        this.window.setSize(width, height);
        //----------------------------------------------------------------------
        this.window.setIgnoreRepaint(true);
        this.window.setTitle("- Game 1.0a");
        this.window.setVisible(false);
        this.window.setFocusable(true);
        this.window.setFocusTraversalKeysEnabled(false);
        //----------------------------------------------------------------------
        // -- NullRepaintManager install...
        //NullRepaintManager.install();
        //----------------------------------------------------------------------
        this.gEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
        this.gDev = this.gEnv.getDefaultScreenDevice();
        this.fullScreen = false;
        // -- Get all avalable display modes...
        updateDisplayMode();
    }

    public final void updateDisplayMode() {
        updateDisplayMode(DM_QUALITY);
    }

    public final void updateModeToQuality() {
        updateDisplayMode(DM_QUALITY);
    }

    public final void updateModeToBalanced() {
        updateDisplayMode(DM_BALANCED);
    }

    public final void updateModeToPerformance() {
        bitDepth = 100;
        updateDisplayMode(DM_PERFORMANCE);
    }

    public final void updateDisplayMode(int dmPerf) {
        displayModes = gDev.getDisplayModes();
        for (DisplayMode displayMode : displayModes) {
            if (width == displayMode.getWidth() && height == displayMode.getHeight()) {
                switch (dmPerf) {
                    case DM_PERFORMANCE:
                        if (bitDepth > displayMode.getBitDepth()) {
                            bitDepth = displayMode.getBitDepth();
                        }
                        if (refreshRate < displayMode.getRefreshRate()) {
                            refreshRate = displayMode.getRefreshRate();
                        }
                        if (bitDepth < 16) {
                            bitDepth = 16;
                        }
                        if (refreshRate > 60) {
                            refreshRate = 60;
                        }
                        break;
                    case DM_BALANCED:
                        if (bitDepth < displayMode.getBitDepth()) {
                            bitDepth = displayMode.getBitDepth();
                        }
                        if (refreshRate < displayMode.getRefreshRate()) {
                            refreshRate = displayMode.getRefreshRate();
                        }
                        if (bitDepth == 32) {
                            bitDepth = 24;
                        }
                        if (refreshRate > 60) {
                            refreshRate = 60;
                        }
                        break;
                    case DM_QUALITY:
                        if (bitDepth < displayMode.getBitDepth() || bitDepth>32) {
                            bitDepth = displayMode.getBitDepth();
                        }
                        if (refreshRate < displayMode.getRefreshRate()) {
                            refreshRate = displayMode.getRefreshRate();
                        }
                        if (refreshRate > 60) {
                            refreshRate = 60;
                        }
                        break;
                    default:
                        bitDepth = DisplayMode.BIT_DEPTH_MULTI;
                        refreshRate = DisplayMode.REFRESH_RATE_UNKNOWN;
                        Logger.logWarning(getClass().getSimpleName() + " - updateDisplayMode(): The perform mode is unknown [" + dmPerf + "]...");
                }
            }
        }
        Logger.logMessage(getClass().getSimpleName() + " - updateDisplayMode(): Actual resolution = "+FlexDisplayMode.format(width, height, bitDepth, refreshRate));
    }
    
    public void setFullScreen(boolean fullScreen, int perform) {
        FlexEngine.getInstance().fullScreenStateChanging();
        Logger.logMessage(getClass().getSimpleName()+" - Changing to fullscreen = "+fullScreen);
        if (window != null) {
            updateDisplayMode(perform);
            try {
                this.fullScreen = fullScreen;
                if( fullScreen ) {
                    Logger.logMessage(getClass().getSimpleName()+" - Setting the WINDOW paramaters...");
                    window.dispose();
                    window.setPreferredSize(new Dimension(width, height));
                    window.setSize(width, height);
                    window.setUndecorated(true);
                    window.setResizable(false);
                    Logger.logMessage(getClass().getSimpleName()+" - FullScreen is supported = "+gDev.isFullScreenSupported());
                    if( gDev.isFullScreenSupported() ) {
                        gDev.setFullScreenWindow(window);
                        Logger.logMessage(getClass().getSimpleName()+" - DisplayMode change is supported = "+gDev.isDisplayChangeSupported());
                        if( gDev.isDisplayChangeSupported() ) {
                            DisplayMode dm = new DisplayMode(width, height, bitDepth, refreshRate);
                            Logger.logMessage(getClass().getSimpleName()+" - Change displayMode to ["+FlexDisplayMode.format(dm.getWidth(), dm.getHeight(), dm.getBitDepth(), dm.getRefreshRate()) +"]");
                            gDev.setDisplayMode(dm);
                        }
                        window.createBufferStrategy(2);
                    } else {
                        this.fullScreen = false;
                    }
                }
            } catch(Exception e) {
                Logger.logError(getClass()+" - setFullScreen(): "+e);
                this.fullScreen = false;
            }
        }
        this.centeredCursor();
        FlexEngine.getInstance().fullScreenStateChanging();
    }
    
    // -------------------------------------------------------------------------
    public void setTitle(String title) {
        if (window != null) {
            window.setTitle(title);
        }
    }

    public void setVisible(boolean visible) {
        if ( visible ) {
            if (!fullScreen) {
                centeredWindow();
            }
            window.setPreferredSize(new Dimension(width, height));
            window.setSize(width, height);
            window.setUndecorated(false);
            window.setResizable(false);
            window.setVisible(visible);
            window.createBufferStrategy(2);
            optimized = true;
        } else {
            if (window != null) {
                window.dispose();
            }
            fullScreen = false;
            optimized = false;
        }
        centeredCursor();
    }

    public boolean isVisibile() {
        if (window != null) {
            return window.isVisible();
        }
        return false;
    }

    public void dispose() {
        if (window != null) {
            window.dispose();
        }
    }

    public void add(Component comp) {
        add(comp, null);
    }

    public void add(Component comp, Object constraints) {
        if (window != null) {
            comp.setIgnoreRepaint(true);
            window.add(comp, constraints);
        }
    }

    public void remove(int index) {
        if (window != null) {
            window.getContentPane().remove(index);
        }
    }

    public void remove(Component comp) {
        if (window != null) {
            window.getContentPane().remove(comp);
        }
    }

    public void removeAll() {
        if (window != null) {
            window.getContentPane().removeAll();
        }
    }

    public synchronized void paintComponents() {
        try {
            if (window != null) {
                // -- Draw AWT or SWING components.
            }
        } catch (Exception e) {
            Logger.logError(getClass().getSimpleName() + " - paintComponents() ERROR = " + e);
        }
    }

    public void setIcon(Texture text) {
        if (window != null && text!=null && text.getImage()!=null) {
            window.setIconImage(text.getImage());
        }
    }

    public boolean isFullScreen() {
        return fullScreen;
    }

    public void destroy() {
        this.fullScreen = false;
        this.gEnv = null;
        this.gDev = null;
        this.window.dispose();
        this.window = null;
    }

    public void setLayout(LayoutManager manager) {
        if (window != null) {
            window.setLayout(manager);
        }
    }

    public GraphicsDevice getGraphicsDevice() {
        return gDev;
    }

    public GraphicsEnvironment getGraphicsEnviroment() {
        return gEnv;
    }

    public boolean isOptimized() {
        return optimized;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public Insets getInsets() {
        if (window != null) {
            return window.getInsets();
        }
        return new Insets(0, 0, 0, 0);
    }

    public int getDrawableWidth() {
        if (window != null && window.getInsets() != null) {
            return width - window.getInsets().left - window.getInsets().right - 1;
        }
        return width;
    }

    public int getDrawableHeight() {
        if (window != null && window.getInsets() != null) {
            return height - window.getInsets().top - window.getInsets().bottom - 1;
        }
        return height;
    }

    public Dimension getSize() {
        return new Dimension(width, height);
    }

    public Point getLocation() {
        if (window != null) {
            return window.getLocationOnScreen();
        }
        return null;
    }

    public void setLocation(int x, int y) {
        if (window != null) {
            window.setLocation(x, y);
        }
    }

    public void setWidth(int width) {
        this.width = width;
        if (window != null) {
            window.setSize(width, height);
        }
    }

    public void setHeight(int height) {
        this.height = height;
        if (window != null) {
            window.setSize(width, height);
        }
    }

    public void setSize(Dimension size) {
        if (size != null) {
            setSize(size.width, size.height);
        }
    }

    public void setSize(int width, int height) {
        if (width >= 0 && height >= 0) {
            this.width = width;
            this.height = height;
            if (window != null) {
                window.setSize(width, height);
            }
        } else {
            Logger.logWarning(getClass().getSimpleName() + " - setSize(): Ivalide arguments - Size[" + width + "," + height + "]...");
        }
    }
    
    public boolean setDisplayMode(FlexDisplayMode displayMode) {
        return setDisplayMode(displayMode.getWidth(), displayMode.getHeight(), displayMode.getBitDepth(), displayMode.getRefreshRate());
    }
    public boolean setDisplayMode(int width, int height, int bitDepth, int refreshRate) {
        this.width = width;
        this.height = height;
        this.bitDepth = bitDepth;
        this.refreshRate = refreshRate;
        if( window!=null ) {
            window.setSize(width, height);
        }
        updateDisplayMode();
        if( this.bitDepth!=bitDepth || this.refreshRate!=refreshRate ) {
            Logger.logWarning(getClass().getSimpleName()+"::setDisplayMode("+FlexDisplayMode.format(width, height, bitDepth, refreshRate) +
                    "): Can't set bitDepth["+bitDepth+"] or refreshRate["+refreshRate+"]."
                    + "\n\tDisplayMode = "+FlexDisplayMode.format(this.width, this.height, this.bitDepth, this.refreshRate));
            return false;
        }
        return true;
    }

    public Color getBackground() {
        if (window != null) {
            //window.getContentPane().getBackground();
            return window.getBackground();
        }
        return null;
    }
    
    public void setBackground(Color color) {
        if (window != null) {
            window.setBackground(color);
        }
    }

    public void setCursor(Cursor cursor) {
        if (window != null) {
            window.setCursor(cursor);
        }
    }

    public void setBitDepth(int bitDepth) {
        this.bitDepth = bitDepth;
    }
    
    public int getBitDepth() {
        return bitDepth;
    }

    public int getRefreshRate() {
        return refreshRate;
    }

    public void setRefreshRate(int refreshRate) {
        this.refreshRate = refreshRate;
    }

    public void centeredCursor() {
        try {
            if (window != null) {
                int x = (window.getWidth() / 2) + window.getLocation().x;
                int y = (window.getHeight() / 2) + window.getLocation().y;
                new Robot().mouseMove(x, y);
            } else {
                Logger.logWarning(getClass().getSimpleName() + ": Can't possible centered the cursor becasue a window does not exist...");
            }
        } catch (AWTException e) {
            Logger.logError(getClass().getSimpleName() + ": " + e.getMessage());
        }
    }

    public void centeredWindow() {
        if (window != null) {
            window.setLocationRelativeTo(null);
        }
    }

    public int getCenterX() {
        try {
            Dimension size = window.getSize();
            int centerX = (int) (size.getWidth() / 2);
            return centerX;
        } catch (Exception e) {
        }
        return -1;
    }

    public int getCenterY() {
        try {
            Dimension size = window.getSize();
            int centerX = (int) (size.getHeight() / 2);
            return centerX;
        } catch (Exception e) {
        }
        return -1;
    }

    public Point getCenterPoint() {
        return new Point(getCenterX(), getCenterY());
    }

    /**
     * Take a screen host of the view game...
     *
     * @param file Name of the file for save the picture.
     */
    public static void captureScreenShot(String file) {
        try {
            Robot robot = new Robot();
            Point loc = (Point) GameWindow.getInstance().getLocation().clone();
            Dimension size = (Dimension) GameWindow.getInstance().getSize().clone();
            if (!GameWindow.getInstance().isFullScreen()) {
                loc.setLocation(loc.x + 1, loc.y + 30);
                size.setSize(size.getWidth() - 2, size.getHeight() - 30);
            }
            // --
            BufferedImage capture = robot.createScreenCapture(new Rectangle(loc, size));
            // --
            File img = new File(file+".bmp");
            img.delete();
            ImageIO.write(capture, "bmp", img);
            Logger.logMessage("Screenshot taked ["+img+"] ...");
        } catch (AWTException | IOException ex) {
            Logger.logError(GameWindow.class.getSimpleName() + ": " + ex.getMessage());
        }
    }

    public Graphics getGraphics() {
        if (window != null) {
            return GraphicsJ2D.getInstance().create();
        }
        return null;
    }

    protected java.awt.Graphics getGraphicsAWT() {
        if (window != null) {
            return window.getGraphics();
        }
        return null;
    }

    protected BufferStrategy getBufferStrategy() {
        if (window != null) {
            return window.getBufferStrategy();
        }
        return null;
    }
    
    public void addKeyListener(KeyListener listener) {
        if( window!=null ) {
            window.addKeyListener(listener);
        }
    }
    public void addMouseListener(MouseListener listener) {
        if( window!=null ) {
            window.addMouseListener(listener);
        }
    }
    public void addMouseWheelListener(MouseWheelListener listener) {
        if( window!=null ) {
            window.addMouseWheelListener(listener);
        }
    }
    public void addMouseMotionListener(MouseMotionListener listener) {
        if( window!=null ) {
            window.addMouseMotionListener(listener);
        }
    }
    public void addWindowListener(WindowListener listener) {
        if( window!=null ) {
            window.addWindowListener(listener);
        }
    }
    
    private static GameWindow instance = null;
    public static GameWindow getInstance() {
        if (instance == null) {
            instance = new GameWindow();
        }
        return instance;
    }
}